﻿using SqlSugar;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace Common.Utils
{
    public static class SnowflakeIdGenerator
    {
        private const int WorkerIdBits = 10;
        private const int SequenceBits = 12;

        // 定义机器标识（取值范围为 0~1023）
        private static readonly int WorkerId = GetWorkerId();

        private static long _lastTimestamp = -1L;
        private static int _sequence = 0;

        private static Regex _digitsOnlyRegex = new Regex(@"\d+");

        public static long GenerateId()
        {
            lock (typeof(SnowflakeIdGenerator))
            {
                long timestamp = GetCurrentTimestamp();
                if (timestamp < _lastTimestamp)
                {
                    throw new Exception($"Invalid system clock: timestamp ({timestamp}) is earlier than previous timestamp ({_lastTimestamp})");
                }
                else if (timestamp == _lastTimestamp)
                {
                    _sequence = (_sequence + 1) & ((1 << SequenceBits) - 1);
                    if (_sequence == 0)
                    {
                        timestamp = WaitNextMillis(_lastTimestamp);
                    }
                }
                else
                {
                    _sequence = 0;
                }

                _lastTimestamp = timestamp;

                long id = (timestamp << (WorkerIdBits + SequenceBits)) | (WorkerId << SequenceBits) | _sequence;

                // 过滤掉符号位和除数字以外的其他字符
                string idStr = id.ToString();
                string digitsOnly = new string(idStr.Where(c => char.IsDigit(c)).ToArray());
                return long.Parse(digitsOnly);
            }
        }

        private static int GetWorkerId()
        {
            string timestampStr = DateTimeOffset.UtcNow.Ticks.ToString().Replace("-", "");
            MD5 md5 = MD5.Create();
            byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(timestampStr));
            int workerId = BitConverter.ToInt32(hash, 0);
            workerId = Math.Abs(workerId % 1024); // 取绝对值，并限制在 0~1023 范围内
            return workerId;
        }

        private static long GetCurrentTimestamp()
        {
            return DateTimeOffset.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
        }

        private static long WaitNextMillis(long lastTimestamp)
        {
            long timestamp = GetCurrentTimestamp();
            while (timestamp <= lastTimestamp)
            {
                timestamp = GetCurrentTimestamp();
            }
            return timestamp;
        }
        
    }
}
